﻿//-----------------------------------------------------------------------
// <copyright>
// This file is subject to the terms and conditions of the Microsoft Public License (MS-PL).
// See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL for more details.
// </copyright>
//----------------------------------------------------------------------- 
using System;
using System.CodeDom.Compiler;
using System.IO;
using System.Text;
using Microsoft.VisualStudio.TextTemplating;

namespace Fulcrum
{
    public class FulcrumEngine
    {
        FulcrumSettings settings;
        GenerationUnit generationUnit;

        public FulcrumEngine(FulcrumSettings settings, GenerationUnit generationUnit)
        {
            if (settings == null)
            {
                throw new ArgumentNullException("settings");
            }

            if (generationUnit == null)
            {
                throw new ArgumentNullException("generationUnit");
            }

            this.settings = settings;
            this.generationUnit = generationUnit;
        }

        public void Run()
        {
            this.generationUnit.CreateDataContexts();

            foreach (var dataContext in this.generationUnit.DataContexts)
            {
                ProcessTemplate(dataContext);
            }
        }

        void ProcessTemplate(GenerationDataContext dataContext)
        {
            var host = new TextTemplatingEngineHost(dataContext);
            Engine engine = new Engine();

            host.TemplateFile = GetTemplateFileName(dataContext);

            string template = GetTemplate(host.TemplateFile);

            string output = engine.ProcessTemplate(template, host);

            bool hasErrors = DetermineIfTemplateProcessingErrors(host);
            
            if (host.Errors.Count > 0)
            {
                if (hasErrors)
                {
                    Console.WriteLine("Errors encountered while processing data context {0}", dataContext.Name);
                }
                else
                {
                    Console.WriteLine("Warnings encountered while processing data context {0}", dataContext.Name);
                }

                foreach (CompilerError error in host.Errors)
                {
                    DisplayErrors(error);
                }
            }


            if(false == hasErrors)
            {
                string outputFileName = GetOutputFileName(dataContext, host);

                if (IsFileReadOnly(outputFileName))
                {
                    Console.WriteLine("ERROR: Can't write to file {0}.", outputFileName);
                }
                else
                {
                    WriteOutputFile(output, outputFileName);
                }
            }
        }

        private static bool IsFileReadOnly(string fileName)
        {
            if (File.Exists(fileName))
            {
                var attribs = File.GetAttributes(fileName);

                if (File.Exists(fileName) && (attribs & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
                {
                    return true;
                }
            }

            return false;
        }

        private static bool DetermineIfTemplateProcessingErrors(TextTemplatingEngineHost host)
        {
            bool hasErrors = false;

            foreach (var error in host.Errors)
            {
                if (error.IsWarning == false)
                {
                    hasErrors = true;
                }
            }
            return hasErrors;
        }

        private static string GetTemplate(string templateFileName)
        {
            string template = File.ReadAllText(templateFileName);

            return template;
        }

        private static void WriteOutputFile(string output, string outputFileName)
        {
            string path = Path.GetDirectoryName(outputFileName);

            if (false == Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }

            File.WriteAllText(outputFileName, output, Encoding.UTF8);
        }

        private string GetOutputFileName(GenerationDataContext dataContext, TextTemplatingEngineHost host)
        {
            string outputFileName = String.Format("{0}{1}", dataContext.OutputFileName, host.OutputFileExtension);

            string outputPath = Path.Combine(this.settings.OutputDirectory, dataContext.OutputPath);

            string completeOutputFileName = Path.Combine(outputPath, outputFileName);

            return completeOutputFileName;
        }

        private static void DisplayErrors(CompilerError error)
        {
            Console.WriteLine(error.ToString());
            Console.WriteLine();
        }

        private static string GetTemplateFileName(GenerationDataContext dataContext)
        {
            string templateFileName = dataContext.TemplateFileName;

            string path = Path.GetDirectoryName(dataContext.GetType().Assembly.Location);

            templateFileName = Path.Combine(path, templateFileName);

            if (String.IsNullOrEmpty(templateFileName))
            {
                throw new ArgumentException("The template file name can't be null or empty.");
            }

            if (!File.Exists(templateFileName))
            {
                throw new FileNotFoundException(String.Format("The template file '{0}' was not found.", templateFileName));
            }

            return templateFileName;
        }
    }
}
